﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class ConvexGenerater {

	public struct ConvexPoly{
		public Vector3 position;
		public Vector3[] verts;
		public float maxH;
		public float minH;
	}
//		
	public List<Vector3> verts = new List<Vector3>();

	public List<int> hulls = new List<int> ();

	public Vector3[] polyVerts;

	public float polyHeight = 6f;

	public float maxHeight = float.MinValue;
	public float minHeight = float.MaxValue;

	public readonly static Color COLOR_GRASS = new Color(0.2f, 0.8f, 0.2f, 1f);
	public readonly static Color COLOR_WATER = new Color(0.2f, 0.2f, 0.8f, 1f);
	public readonly static Color COLOR_ROAD = new Color(0.6f, 0.8f, 0.2f, 1f);

	public static Vector3 GetPolyCenter(List<Vector3> verts){
		Vector3 t = Vector3.zero;
		foreach (var v in verts) {
			t += v;
		}
		return t / (float)verts.Count;
	}

	public static Vector3 GetPolyCenter(Vector3[] verts){
		Vector3 t = Vector3.zero;
		foreach (var v in verts) {
			t += v;
		}
		return t / (float)verts.Length;
	}


	private static bool left(Vector3 a, Vector3 b, Vector3 c)
	{ 
		float u1 = b[0] - a[0];
		float v1 = b[2] - a[2];
		float u2 = c[0] - a[0];
		float v2 = c[2] - a[2];
		return u1 * v2 - v1 * u2 < 0;
	}
		
	private static bool cmppt(Vector3 a, Vector3 b)
	{
		if (a[0] < b[0]) return true;
		if (a[0] > b[0]) return false;
		if (a[2] < b[2]) return true;
		if (a[2] > b[2]) return false;
		return false;
	}

	private static bool PointInPoly(int nvert, List<Vector3> verts, Vector3 p)
	{
		int i, j;
		bool c = false;
		for (i = 0, j = nvert-1; i < nvert; j = i++)
		{
			Vector3 vi = verts[i];
			Vector3 vj = verts[j];
			if (((vi[2] > p[2]) != (vj[2] > p[2])) && (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
				c = !c;
		}
		return c;
	}

	private static int ConvexHull(List<Vector3> pts, ref List<int> hulls)
	{
		int npts = pts.Count;
		int hull = 0;
		for (int i = 1; i < npts; ++i)
			if (cmppt(pts[i], pts[hull]))
				hull = i;
		int endpt = 0;
		hulls.Clear ();
		int first = hull;
		do
		{
			hulls.Add(hull);
			endpt = 0;
			for (int j = 1; j < npts; ++j)
				if (hull == endpt || left(pts[hull], pts[endpt], pts[j]))
					endpt = j;
			hull = endpt;
		}while (endpt != first);
		return hulls.Count;
	}

	public void ReHull(){
		
		int lv = verts.Count;
		if (lv > 1) {
			ConvexHull (verts, ref hulls);
		}
		maxHeight = float.MinValue;
		minHeight = float.MaxValue;
		polyVerts = new Vector3[hulls.Count];
		for (int i = 0; i < hulls.Count; ++i) {
			polyVerts[i] = verts[hulls [i]];
			if (polyVerts [i].y > maxHeight) {
				maxHeight = polyVerts [i].y;
			}
			if (polyVerts [i].y < minHeight) {
				minHeight = polyVerts [i].y;
			}
		}
	}

	public void Clean(){
		verts.Clear ();
		hulls.Clear ();
	}

	public void AddVertex(Vector3 point){
		verts.Add (point);
		ReHull ();
	}


	public void RemoveSelectionVertex(){
		if (selectVertex >= 0) {
			verts.RemoveAt (selectVertex);
			ReHull ();
			selectVertex = -1;
		}
	}

	public void RemoveVertex(Vector3 point){
		
	}

	public IEnumerable<Vector3> VisitPolyVertex() {
		foreach (var h in hulls) {
			yield return verts [h];
		}
	}

	public ConvexPoly ToConvexPoly(){
		return new ConvexPoly (){ position = Vector3.zero, verts = this.polyVerts, minH = minHeight, maxH = Mathf.Max(maxHeight, minHeight + polyHeight)};
	}

	public void ToMesh(ref Mesh mesh){
		var vfc = GetPolyCenter (polyVerts);


		List<int> tis = new List<int> ();
		List<Vector3> vs = new List<Vector3> ();
		vs.Add (vfc);
		vs.AddRange (polyVerts);
        int fc = 0,  fmini = 1;
		int fmaxi = vs.Count - 1;
		for (int i = fmini; i <= fmaxi; ++i)
        {
			tis.Add (fc);
            tis.Add(i);
            tis.Add (i == fmaxi ? fmini : i + 1);
        }
    
        var vcc = vfc + Vector3.up * polyHeight;
        int cc = vs.Count;
        vs.Add(vcc);
        int cmini = vs.Count;
        for (int i = 0; i < polyVerts.Length; ++i)
        {
            vs.Add(new Vector3(polyVerts[i].x, polyVerts[i].y + polyHeight, polyVerts[i].z));
        }
        int cmaxi = vs.Count - 1;
        for (int i = cmini; i <= cmaxi; ++i)
        {
            tis.Add(cc);
            tis.Add(i == cmaxi ? cmini : i + 1);
            tis.Add(i);
        }

        for (int fi = fmini; fi <= fmaxi; ++fi) { 
            int ci = cmini + fi - fmini;
            tis.Add(ci);
            tis.Add(ci == cmaxi ? cmini : ci + 1);
            tis.Add(fi);

            tis.Add(fi);
            tis.Add(ci == cmaxi ? cmini : ci + 1);
            tis.Add(ci == cmaxi ? fmini : fi + 1);
        }


        mesh.SetVertices(vs);
        mesh.SetTriangles(tis, 0);

    }

	#if UNITY_EDITOR
	public int selectVertex = -1;

	public int SelectVertex(Vector3 point){
		for(int i = 0; i < verts.Count; ++i){
			if ((point - verts [i]).sqrMagnitude < 0.2) {
				return i;
			}
		}
		return -1;
	}

	public void OnSceneViewGUI(SceneView view, out bool dirty){
		//draw vertex
		dirty = false;
		for(int i = 0; i < verts.Count; ++i){
			Handles.color = i == selectVertex ? Color.red : Color.black;
			Handles.DrawSolidDisc (verts[i], Vector3.up, 0.2f);
			if (i == selectVertex) {
				Vector3 pos = Handles.PositionHandle (verts [i], Quaternion.identity);
				if ((pos - verts [i]).sqrMagnitude > 0.01) {
					verts [i] = pos;
					dirty = true;
				}
			}
		}
		if (dirty) {
			ReHull ();
		}

        if (polyVerts.Length == 0) {
            return;
        }
        Color colPoly = COLOR_WATER;
        Vector3[] ceils = new Vector3[polyVerts.Length]; 
		Vector3[] v4 = new Vector3[4];
		for (int i = 0; i < polyVerts.Length; ++i) {
			ceils[i] = new Vector3 (polyVerts [i].x, polyVerts [i].y + polyHeight, polyVerts [i].z);
			Handles.color = colPoly * 0.8f;
			int p = i == 0 ? polyVerts.Length - 1 : i - 1;
			v4 [0] = polyVerts [i];
			v4 [1] = polyVerts [p];
			v4 [2] = new Vector3 (polyVerts [p].x, polyVerts [p].y + polyHeight, polyVerts [p].z);
			v4 [3] = ceils[i];
			Handles.DrawAAConvexPolygon (v4);

			Handles.color = new Color (0.7f, 0.7f, 0.7f, 1f);
			Handles.DrawLine (v4 [0], v4 [3]);
			Handles.DrawLine (v4 [1], v4 [2]);

		}
	
		Handles.color = colPoly;
		Handles.DrawAAConvexPolygon (ceils);

		Handles.color = Color.gray;
		Handles.DrawPolyLine (polyVerts);
		Handles.DrawPolyLine (ceils);

	}


	#endif

}
